package mysgoog.bot.algo;

import java.util.ArrayList;
import java.util.HashSet;

import mysgoog.board.Dragon;
import mysgoog.board.DragonBoard;
import mysgoog.gtp.Color;
import mysgoog.util.Lib;

public class Evaluator {

	private DragonBoard board;
	private double dFactor = 3;
	public static final int LOW = 1;
	public static final int HIGH = 3;
	public static final int MID = 2;
	private static final int turnThres1 = 40;
	private static final int turnThres2 = 80;
	int tsn;

	// private int eyedif;
	private double pondif;

	public Evaluator(DragonBoard b) {
		board = b;
		tsn = -1;
	}

	public int evaluateGrade(int pos, Color color) {
		pondif = evaluate(pos, color);
		// pondif += eyedif * 4;
		if (board.getTurn() <= turnThres1) {
			if (pondif > 8)
				return HIGH;
			else if (pondif > 1)
				return MID;
			else
				return LOW;
		} else if (board.getTurn() <= turnThres2) {
			if (pondif > 5)
				return HIGH;
			else if (pondif > 0.2)
				return MID;
			else
				return LOW;
		} else {
			if (pondif > 3)
				return HIGH;
			else if (pondif > 0)
				return MID;
			else
				return LOW;
		}
	}

	public double evaluate(int pos, Color color) {

		if (pos == -1)
			return -10;

		double ret = 0;

		ArrayList<Integer> arounds = board.getAround(pos);
		arounds.add(pos);
		double[] values = new double[9];

		for (int i = 0; i < arounds.size(); i++) {
			int apos = arounds.get(i);
			Dragon d = board.territoryAt(apos);
			if (d != null) {
				values[i] = d.getPON() * (d.color == color ? 1 : -1);
			} else
				values[i] = 0;
		}

		board.play(color, pos);

		HashSet<Dragon> avoidDup = new HashSet<Dragon>();
		for (int i = 0; i < arounds.size(); i++) {
			int apos = arounds.get(i);
			Dragon d = board.territoryAt(apos);

			if (d != null && !avoidDup.contains(d)) {
				avoidDup.add(d);
				ret += (d.getPON() * (d.color == color ? 1 : -1) - values[i]);
			}
		}

		// if (board.dragonAt(pos).isDangerous())
		// ret -= 3;
		if (board.dragonAt(pos).isDead())
			ret -= 3;

		board.undo();

		return ret;
	}

	public double connEvaluate(int pos, Color color) {

		if (pos == -1)
			return -10;

		double ret = 0;

		ArrayList<Integer> arounds = board.getAround(pos);

		Dragon[] oldDragon = new Dragon[9];
		int[] oldstones = new int[9];
		Dragon nd;

		for (int i = 0; i < arounds.size(); i++) {
			int apos = arounds.get(i);
			oldDragon[i] = board.territoryAt(apos);
			oldstones[i] = 0;
			if (oldDragon[i] != null)
				oldstones[i] = oldDragon[i].getStones().size();

		}

		board.play(color, pos);

		nd = board.territoryAt(pos);

		HashSet<Dragon> avoidDup = new HashSet<Dragon>();
		Dragon old1;
		int os1;
		int newSize = nd.getStones().size();
		int tmp;

		if (newSize >= 3 && nd.color == color) {

			ret = 1;
			tmp = 0;

			for (int i = 0; i < arounds.size(); i++) {
				old1 = oldDragon[i];
				os1 = oldstones[i];

				if (old1 != null && old1.color == color && (newSize - os1) > 1)
					if (!avoidDup.contains(old1)) {
						avoidDup.add(old1);
						ret *= os1;
						tmp += os1;
					}
			}

			if (newSize - tmp > 1)
				ret = ret * (newSize - tmp);

			if (avoidDup.size() == 1)
				ret = 1;
		}

		board.undo();

		return ret;
	}

	public boolean easyEvaluate(int pos, Color color) {
		double ret = 0;

		ret = calcInfuluence(pos);

		if (Math.abs(ret) > 6.5)
			return false;

		if (board.isCorner(pos))
			ret = -1;

		if (board.isEdge(pos)) {
			if (Math.abs(ret) > 4)
				return false;
		}
		return true;
	}

	/**
	 * @author Gorden
	 * @param pos
	 * @return a value, positive if white has more influence on it negative if
	 *         black has more influence on it. the value if larger, the
	 *         influence if bigger. use O(10^2) time -10 < inf <10
	 */
	public double calcInfuluence(int pos) {
		double ret = 0;

		ArrayList<Integer> neighbors = board.getAround(pos, 4);
		for (int k = 0; k < neighbors.size(); k++) {
			int apos = neighbors.get(k);
			double ori = 0;
			double permeate = 0;
			if (board.getStone(apos) == Color.WHITE) {
				ori = 10 / (Math.pow(dFactor, dis(pos, apos)));
				permeate = perm(pos, apos, Color.BLACK);
				ori = ori * (1 - permeate / 2);
			} else if (board.getStone(apos) == Color.BLACK) {
				ori = -10 / (Math.pow(dFactor, dis(pos, apos)));
				permeate = perm(pos, apos, Color.WHITE);
				ori = ori * (1 - permeate / 2);
			}

			ret += ori;
		}

		return ret;
	}

	private double dis(int pos, int apos) {
		double dis = 0;
		int x1 = board.posX(pos), y1 = board.posY(pos), x2 = board.posX(apos), y2 = board
				.posY(apos);
		int xabs = Math.abs(x1 - x2), yabs = Math.abs(y1 - y2);

		dis = Math.max(xabs, yabs) + Math.min(xabs, yabs) * Math.log(2)
				/ Math.log(3);

		return dis;
	}

	private double perm(int pos, int apos, Color c) {
		int x1 = board.posX(pos), y1 = board.posY(pos), x2 = board.posX(apos), y2 = board
				.posY(apos);
		int small, large;

		if (x1 == x2) {
			if (y1 < y2) {
				small = y1;
				large = y2;
			} else {
				small = y2;
				large = y1;
			}

			for (int tmpy = small + 1; tmpy < large; tmpy++) {
				if (board.getStone(board.makePos(x1, tmpy)) == c)
					return 1;
			}
			return 0;
		} else if (y1 == y2) {
			if (x1 < x2) {
				small = x1;
				large = x2;
			} else {
				small = x2;
				large = x1;
			}

			for (int tmpy = small + 1; tmpy < large; tmpy++) {
				if (board.getStone(board.makePos(x1, tmpy)) == c)
					return 1;
			}
			return 0;
		} else {
			int xstep, ystep, p = 0, q = 0;
			if (x1 < x2) {
				xstep = -1;
			} else {
				xstep = 1;
			}

			if (y1 < y2) {
				ystep = -1;
			} else {
				ystep = 1;
			}
			// first, go through x, then go through y.
			for (int xtmp = x2 + xstep; xtmp != x1; xtmp = xtmp + xstep) {
				int pp = board.makePos(xtmp, y2);
				if (board.getStone(pp) == c
						&& ((board.wormAt(pp).getLibs().size() > 2) || (board
								.dragonAt(pp).getEyes().size() > 1))) {
					p = 1;
					break;
				}
			}
			for (int ytmp = y2; ytmp != y1; ytmp = ytmp + ystep) {
				int pp = board.makePos(x1, ytmp);

				if (board.getStone(pp) == c
						&& ((board.wormAt(pp).getLibs().size() > 2) || (board
								.dragonAt(pp).getEyes().size() > 1))) {
					p = 1;
					break;
				}
			}
			// second, go through y, then go through x.
			for (int ytmp = y2 + ystep; ytmp != y1; ytmp = ytmp + ystep) {
				int pp = board.makePos(x1, ytmp);
				if (board.getStone(pp) == c
						&& ((board.wormAt(pp).getLibs().size() > 2) || (board
								.dragonAt(pp).getEyes().size() > 1))) {
					q = 1;
					break;
				}
			}
			for (int xtmp = x2; xtmp != x1; xtmp = xtmp + xstep) {
				int pp = board.makePos(xtmp, y1);
				if (board.getStone(pp) == c
						&& ((board.wormAt(pp).getLibs().size() > 2) || (board
								.dragonAt(pp).getEyes().size() > 1))) {
					q = 1;
					break;
				}
			}
			return p + q;
		}
	}
}
